﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Xml.Linq;

namespace ClientApplication
{
    public class Server
    {
        bool run;
        int standardPort;
        string filepathLogin;
        string filePathXMLResults;
        public UdpClient client { get; private set; }
        public IPEndPoint endPoint { get; private set; }
        public Dictionary<String, PassAndAccess> logins;

        List<ContestPacket> activeContestPackets;
        List<ContestPacket> resultsList;

        //Different kinds of queues.
        //"Message" is a nested class in server.
        MessageQueue messageQueueContest;
        MessageQueue messageQueuePing;
        MessageQueue messageQueueLogin;
        MessageQueue messageQueueAdmin;

        public List<Judge> availableJudgePool { get; private set; }
        public List<Judge> activeJudgePool { get; private set; }
        public List<Judge> adminPool { get; private set; }

        //----------------------------------------------------------------------------//

        public Server()
        {
            activeContestPackets = new List<ContestPacket>();
            resultsList = new List<ContestPacket>();

            messageQueueContest = new MessageQueue();
            messageQueuePing = new MessageQueue();
            messageQueueLogin = new MessageQueue();
            messageQueueAdmin = new MessageQueue();

            availableJudgePool = new List<Judge>();
            activeJudgePool = new List<Judge>();
            adminPool = new List<Judge>();
            filepathLogin = @"c:\login.txt";
            filePathXMLResults =  @"c:\SerializationResults.xml";
            run = true;
            standardPort = 9057;
            endPoint = new IPEndPoint(IPAddress.Any, standardPort);
            client = new UdpClient(endPoint);

            logins = new Dictionary<string, PassAndAccess>();
           

            LoadResultsXMLFile(filePathXMLResults);
            LoadClientLogin(filepathLogin);
        }
        /// <summary>
        /// Waits for a message on the port bound to the UdpClient
        /// IPEndPoint useless?
        /// Returns the byte[] in a ASCII coded string.
        /// </summary>
        /// <param name="udpClient"></param>
        /// <param name="getMessageEp"></param>
        /// <returns></returns>
        public string GetMessage(ref IPEndPoint sender, int timeOut = 0, string s = "Waiting for conntection...")
        {
            byte[] data = new byte[1024];
            client.Client.ReceiveTimeout = timeOut; //Sets the timeout to infinite unless specified by argument otherwise.

            //Console.WriteLine(s);
            data = client.Receive(ref sender);

            string message = Encoding.ASCII.GetString(data);

            return message;
        }
        /// <summary>
        /// Sends string s to ipEndPoint reciever from port bound by UdpClient.
        /// </summary>
        /// <param name="udpClient"></param>
        /// <param name="s"></param>
        /// <param name="reciever"></param>
        public void SendMessage(string s, IPEndPoint reciever)
        {
            byte[] data = new byte[1024];
            data = Encoding.ASCII.GetBytes(s);
            client.Send(data, data.Length, reciever);
        }
        /// <summary>
        /// Returns true if username and password finds a match.
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        private int CheckLoginCredentials(String username, String password)
        {
            try
            {
                PassAndAccess temp;
                logins.TryGetValue(username, out temp);
                if (temp.password == password)
                    if (temp.access == 1)
                        return 1;
                    else if (temp.access == 2)
                        return 2;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Data);
            }



            return 0;
        }
        private int CheckLoginActiveJudge(Judge judge)
        {
            foreach (var contestPacket in activeContestPackets)
            {
                foreach (var j in contestPacket.judges)
                {
                    if (judge.username == j.username)
                        return 1;
                }
            }
            return 0;
        }
        /// <summary>
        /// Starts threads to for the server
        /// </summary>
        public void StartServer()
        {
            Thread recieveMessageThread = new Thread(SortMessage);
            recieveMessageThread.IsBackground = true;
            recieveMessageThread.Start();

            Thread handleNewConnectionThread = new Thread(Login);
            handleNewConnectionThread.IsBackground = true;
            handleNewConnectionThread.Start();

            Thread handleExistingConnectionsThread = new Thread(Ping);
            handleNewConnectionThread.IsBackground = true;
            handleExistingConnectionsThread.Start();

            Thread handleAdminRequestThread = new Thread(AdminMessages);
            handleAdminRequestThread.IsBackground = true;
            handleAdminRequestThread.Start();

            Thread handleConsoleInputThread = new Thread(ConsoleInput);
            handleConsoleInputThread.IsBackground = true;
            handleConsoleInputThread.Start();

        }
        /// <summary>
        /// Recieves and sorts messages for the server class.
        /// </summary>
        public void SortMessage()
        {
            Message incoming;
            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
            bool loggedIn;
            while (true)
            {
                loggedIn = false;
                int type = 0;
                string temp = GetMessage(ref sender);
                incoming = new Message(temp, sender);

                //Check if sender is logged in.
                loggedIn = LoggedInCheck(incoming.judge.username);
                //Send first letter to determine type
                type = MessageType(temp[0]);

                //If sender is a judge add it to that judges messageQue.
                if (type == 1)
                {
                    SortContestMessages(incoming);
                }
                //If sender is not logged in, login message
                else if (type == 2)
                {
                    messageQueuePing.Enqueue(incoming);
                }
                else if (type == 3)
                {
                    messageQueueLogin.Enqueue(incoming);
                }
                else if (type == 4)
                {
                    messageQueueAdmin.Enqueue(incoming);
                }
            }
        }
        private int MessageType(char firstLetter)
        {
            if (firstLetter == '1')
                return 1;
            if (firstLetter == '2')
                return 2;
            if (firstLetter == '3')
                return 3;
            if (firstLetter == '4')
                return 4;
            if (firstLetter == '5')
                return 5;
            if (firstLetter == '6')
                return 6;
            if (firstLetter == '7')
                return 7;
            return 0;
        }
        public bool LoggedInCheck(string username)
        {
            foreach (Judge judge in activeJudgePool)
            {
                if (judge.username == username)
                    return true;
            }
            foreach (Judge judge in availableJudgePool)
            {
                if (judge.username == username)
                    return true;
            }
            foreach (Judge judge in adminPool)
            {
                if (judge.username == username)
                    return true;
            }
            return false;
        }
        private void AdminStartContest(Message adminRequest)
        {
            /////////New thread, start contest////////
            try
            {
                ContestPacket contestPacket = activeContestPackets.Find(x => x.Admin.username == adminRequest.judge.username);

                if (contestPacket.contest.contestantList.contestants.Count > 0 && contestPacket.judges.Count > 0)
                {
                    Thread fireUpContest = new Thread(() => StartContest(ref contestPacket));
                    fireUpContest.IsBackground = true;
                    fireUpContest.Start();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
        /// <summary>
        /// Sends an XML string on the current available judges
        /// </summary>
        /// <param name="adminRequest"></param>
        public void AdminSendAvailableJudges(Message adminRequest)
        {
            List<JudgeSerializable> jListSerializable = new List<JudgeSerializable>();
            //Convert List<Judge> to List<JudgeSerializable>
            foreach (Judge judge in availableJudgePool)
            {
                JudgeSerializable toAdd = new JudgeSerializable();
                toAdd.convert(judge);
                jListSerializable.Add(toAdd);
            }


            //Serialize List<JudgeSerializeable>
            string sJudges = Serialize<List<JudgeSerializable>>(jListSerializable);
            SendMessage("ConnectedJudges@@" + sJudges, adminRequest.judge.ipEp);
        }
        /// <summary>
        /// Sets the rules for the admins competition
        /// </summary>
        /// <param name="adminRequest"></param>
        public void AdminSetRules(Message adminRequest)
        {
            String[] split = adminRequest.message.Split('$');
            ContestPacket contestPacket;

            //Deserialize
            Rules rules = Deserialize<Rules>(split[1]);

            //Try to find an existing contest with admin.id. If no such contest can be found, create one.
            try
            {
                contestPacket = activeContestPackets.Find(x => x.id == adminRequest.judge.username);
                contestPacket.contest.SetUpRules(rules);
            }
            catch
            {
                contestPacket = new ContestPacket(adminRequest.judge.username);
                contestPacket.contest.SetUpRules(rules);
                contestPacket.Admin = adminRequest.judge;

                activeContestPackets.Add(contestPacket);
            }
        }
        /// <summary>
        /// Adds the contestants to the current contest
        /// </summary>
        /// <param name="adminRequest"></param>
        public void AdminSetContestants(Message adminRequest)
        {
            String[] split = adminRequest.message.Split('$');
            ContestPacket contestPacket;
            ContestantList contestantList = new ContestantList();

            //Deserialize
            contestantList = Deserialize<ContestantList>(split[1]);

            //Try to add contest to a contest with id == admin.id. Must exisit in order for it to be a valid message
            try
            {
                contestPacket = activeContestPackets.Find(x => x.id == adminRequest.judge.username);

                contestPacket.contest.contestantList.contestants.Clear();

                foreach (Contestant contestant in contestantList.contestants)
                {
                    contestPacket.contest.contestantList.contestants.Add(contestant);
                }
            }
            catch
            {
                Console.WriteLine("Tried to add contestants to a non-existing contest.");
                //If null, do nothing. Contest must exist if this request is made.
            }
        }
        /// <summary>
        /// Handle start request from admin. Check available judges  and if they are available start the competition otherwise send reply back to admin.
        /// </summary>
        /// <param name="adminRequest"></param>
        public void AdminCheckJudgesAndStart(Message adminRequest)
        {
            try
            {
                ContestPacket contestPacket = activeContestPackets.Find(x => x.id == adminRequest.judge.username);

                //If rules is set and contest have contestants.
                if (contestPacket.contest.rulesSet && contestPacket.contest.contestantList.contestants.Count > 0 && !contestPacket.contest.started)
                {
                    List<String> usernames = new List<String>();
                    List<JudgeSerializable> jListSerializeable;
                    String[] split = adminRequest.message.Split('$');


                    jListSerializeable = Deserialize<List<JudgeSerializable>>(split[1]);


                    foreach (JudgeSerializable judge in jListSerializeable)
                    {
                        usernames.Add(judge.username);
                    }

                    List<String> available = JudgesAvailable(usernames);

                    if (usernames.Count == available.Count)
                    {
                        contestPacket.judges = new List<Judge>(MoveFromAvailableToActive(usernames));

                        SendMessage("ContestPacket@@" + Serialize<ContestPacket>(contestPacket), adminRequest.judge.ipEp);
                    }
                    else
                    {
                        //Judges not available.
                        SendMessage("Judges not available, please try again@@", adminRequest.judge.ipEp);
                        Console.WriteLine("Judges not available, cant start contest");
                    }

                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Data);
            }
        }
        /// <summary>
        /// Sends a id result list to the client
        /// </summary>
        /// <param name="adminRequest"></param>
        public void AdminSendResultIDList(Message adminRequest)
        {
            List<String> resultsNames = new List<String>();

            for (int i = 0; i < resultsList.Count; i++)
            {
                resultsNames.Add(resultsList[i].contest.rules.date.ToString() + "&" + resultsList[i].contest.rules.place + "&" + resultsList[i].contest.rules.height + "&" + resultsList[i].GUID.ToString() + "&" + resultsList[i].contest.rules.sex.ToString());
            }
            SendMessage("AllFinishedContests@@" + Serialize<List<String>>(resultsNames), adminRequest.judge.ipEp);
        }
        /// <summary>
        /// Sends specified xmlresult on request.
        /// </summary>
        /// <param name="adminRequest"></param>
        public void AdminSendSpecifiedResult(Message adminRequest)
        {
            String[] split = adminRequest.message.Split('&');

            try
            {
                SendMessage("ContestResult@@" + Serialize<ContestPacket>(resultsList.Find(x => x.GUID == Guid.Parse(split[1]))), adminRequest.judge.ipEp);
            }
            catch (Exception e)
            {
                SendMessage("CannotFindRequestedResult@@", adminRequest.judge.ipEp);
                Console.WriteLine(e.Data);
            }
        }
        /// <summary>
        /// ----------------------Prototype functions for new server----------------
        /// </summary>
        /// 
        public void AdminMessages()
        {
            while (true)
            {
                Thread.Sleep(2000);
                while (messageQueueAdmin.Count > 0)
                {
                    Message adminRequest = messageQueueAdmin.Dequeue();

                    int type = MessageType(adminRequest.message[0]);

                    if (type == 1)
                    {
                        AdminSendAvailableJudges(adminRequest);
                    }
                    else if (type == 2)
                    {
                        AdminSetRules(adminRequest);
                    }
                    else if (type == 3)
                    {
                        AdminSetContestants(adminRequest);
                    }
                    else if (type == 4)
                    {
                        AdminCheckJudgesAndStart(adminRequest);
                    }
                    else if (type == 5)
                    {
                        AdminSendResultIDList(adminRequest);
                    }
                    else if (type == 6)
                    {
                        AdminSendSpecifiedResult(adminRequest);
                    }
                    else if (type == 7)
                    {
                        AdminStartContest(adminRequest);   
                    }
                }
            }
        }
        public void Login()
        {
            bool loggedIn = false;
            messageQueueLogin.StartListen();
            while (run)
            {
                Thread.Sleep(2000);
                if (messageQueueLogin.Count > 0)
                {
                    Message message = messageQueueLogin.Dequeue();

                    //Check if user is already logged in.
                    loggedIn = LoggedInCheck(message.judge.username);

                    if (loggedIn == false)
                    {
                        int accessLevel = CheckLoginCredentials(message.judge.username, message.message);

                        
                        //If logged in judge is an active judge
                        if (CheckLoginActiveJudge(message.judge) == 1)
                        {
                            lock (activeJudgePool)
                            {
                                SendMessage("LogIn@SERVER@Judge", message.judge.ipEp);
                                Console.WriteLine("Judge " + message.judge.username + " connected.");
                                activeJudgePool.Add(new Judge(message.judge.username, message.judge.ipEp, message.message));

                                try
                                {
                                    for (int i = 0; i < activeContestPackets.Count; i++)
                                    {
                                        for (int j = 0; j < activeContestPackets[i].judges.Count; j++)
                                        {
                                            if (message.judge.username == activeContestPackets[i].judges[j].username)
                                                SendMessage("JudgeOnline@SERVER@" + message.judge.username, activeContestPackets[i].Admin.ipEp);
                                        }
                                    }
                                }
                                catch (Exception e)
                                {
                                    Console.WriteLine(e.Message);
                                }
                            }
                        }
                        else if (accessLevel == 1)
                        {
                            lock (availableJudgePool)
                            {
                                SendMessage("LogIn@SERVER@Judge", message.judge.ipEp);
                                Console.WriteLine("Judge " + message.judge.username + " connected.");
                                availableJudgePool.Add(new Judge(message.judge.username, message.judge.ipEp, message.message));
                            }
                        }
                        else if (accessLevel == 2)
                        {
                            lock (adminPool)
                            {
                                SendMessage("LogIn@SERVER@Admin", message.judge.ipEp);
                                Console.WriteLine("Admin " + message.judge.username + " connected as an admin.");
                                adminPool.Add(new Judge(message.judge.username, message.judge.ipEp, message.message));
                            }
                        }
                        else
                        {
                            SendMessage("Not valid@SERVER@", message.judge.ipEp);
                            Console.WriteLine("Login atempt with username" + message.judge.username +  "failed. IP: " + message.judge.ipEp.ToString());
                        }
                    }
                }
            }
            messageQueueLogin.StopListen();
        }
        /// <summary>
        /// Generic deseralize method.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="input"></param>
        /// <returns></returns>
        public T Deserialize<T>(string input)
        where T : class
        {
            System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(T));

            using (StringReader sr = new StringReader(input))
                return (T)ser.Deserialize(sr);
        }
        /// <summary>
        /// Generic serialize method.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="toSerialize"></param>
        /// <returns></returns>
        public String Serialize<T>(T toSerialize)
        where T : class
        {
            string xmlString;
            XmlSerializer writer = new XmlSerializer(toSerialize.GetType());
            using (System.IO.StringWriter file = new System.IO.StringWriter())
            {
                writer.Serialize(file, toSerialize);
                xmlString = file.ToString();
            }

            return xmlString;

        }
        public void Ping()
        {
            while (run)
            {
                Thread.Sleep(1000);

                //Ping the availableJudges
                #region availableJudgePool
                
                if (availableJudgePool.Count > 0)
                {
                    lock (availableJudgePool)
                    {
                        messageQueuePing.StartListen(); //Open for messages of type 3

                        Broadcast("Update your status@@", availableJudgePool, 2000);


                        List<Judge> responders = new List<Judge>();
                        while (messageQueuePing.Count > 0)
                        {
                            Message message = messageQueuePing.Dequeue();

                            Judge temp = new Judge(message.judge.username, message.judge.ipEp, message.message);


                            //If temp is logged in and is not already added to newConnections.
                            if (!responders.Contains(temp))
                            {
                                responders.Add(temp);
                            }
                        }
                        //Snittet av de två listorna läggs in i tempList
                        List<Judge> tempList = new List<Judge>();

                        tempList = new List<Judge>(availableJudgePool);

                        foreach (var judge in responders)
                        {
                            try
                            {
                                foreach (var judge2 in tempList)
                                {
                                    if (judge.username.CompareTo(judge2.username) == 0)
                                    {
                                        tempList.Remove(judge2);
                                        break;
                                    }
                                }
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e.Message);
                            }
                        }

                        foreach (var judge in tempList)
                        {
                            Console.WriteLine("Judge " + judge.username + " was disconnected.");
                        }


                        availableJudgePool = new List<Judge>(responders);

                        responders.Clear();

                        messageQueuePing.StopListen();
                    }
                }
                #endregion availableJudgePool

                //Ping the activeJudges
                #region activeJudgePool
                if (activeJudgePool.Count > 0)
                {
                    lock (activeJudgePool)
                    {
                        messageQueuePing.StartListen(); //Open for messages of type 3

                        Broadcast("Update your status@@", activeJudgePool, 2000);

                        List<Judge> responders = new List<Judge>();
                        while (messageQueuePing.Count > 0)
                        {
                            Message message = messageQueuePing.Dequeue();


                            Judge temp = new Judge(message.judge.username, message.judge.ipEp, message.message);


                            //If temp is logged in and is not already added to newConnections.
                            if (!responders.Contains(temp))
                            {
                                responders.Add(temp);
                            }
                        }
                        //Snittet av de två listorna läggs in i tempList
                        List<Judge> tempList = new List<Judge>();

                        tempList = new List<Judge>(activeJudgePool);

                        foreach (var judge in responders)
                        {
                            try
                            {
                                foreach (var judge2 in tempList)
                                {
                                    if (judge.username.CompareTo(judge2.username) == 0)
                                    {
                                        tempList.Remove(judge2);
                                        break;
                                    }
                                }
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e.Message);
                            }
                        }

                        foreach (var judge in tempList)
                        {
                            Console.WriteLine("Judge " + judge.username + " was disconnected.");
                        }
                        activeJudgePool = new List<Judge>(responders);

                        responders.Clear();

                        messageQueuePing.StopListen();
                    }
                }
                #endregion availableJudgePool

                //Ping the admins
                #region adminPool
                if (adminPool.Count > 0)
                {
                    lock (adminPool)
                    {
                        messageQueuePing.StartListen(); //Open for messages of type 3

                        Broadcast("Update your status@@", adminPool, 2000);

                        List<Judge> responders = new List<Judge>();
                        while (messageQueuePing.Count > 0)
                        {
                            String[] split;
                            Message message = messageQueuePing.Dequeue();
                            split = message.message.Split('@');


                            Judge temp = new Judge(message.judge.username, message.judge.ipEp, message.message);


                            //If temp is logged in and is not already added to newConnections.
                            if (!responders.Contains(temp))
                            {
                                responders.Add(temp);
                            }
                        }
                        //Snittet av de två listorna läggs in i tempList
                        List<Judge> tempList = new List<Judge>();

                        tempList = new List<Judge>(adminPool);

                        foreach (var judge in responders)
                        {
                            try
                            {
                                foreach (var judge2 in tempList)
                                {
                                    if (judge.username.CompareTo(judge2.username) == 0)
                                    {
                                        tempList.Remove(judge2);
                                        break;
                                    }
                                }
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e.Message);
                            }
                        }

                        foreach (var judge in tempList)
                        {
                            Console.WriteLine("Admin " + judge.username + " was disconnected.");
                        }
                        adminPool = new List<Judge>(responders);

                        responders.Clear();

                        messageQueuePing.StopListen();
                    }
                }
                #endregion adminPool

            }

        }
        /// <summary>
        /// Check if active judges is online.
        /// </summary>
        /// <param name="contestPacket"></param>
        private void StartContestCheckJudgesOnline(ref ContestPacket contestPacket)
        {
            List<Judge> tempList = new List<Judge>();

            tempList = new List<Judge>(contestPacket.judges);

            foreach (var judge in activeJudgePool)
            {
                try
                {
                    foreach (var judge2 in tempList)
                    {
                        if (judge.username.CompareTo(judge2.username) == 0)
                        {
                            tempList.Remove(judge2);
                            break;
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
            }

            foreach(Judge j in tempList)
            {
                SendMessage("JudgeOffline@SERVER@" + j.username, contestPacket.Admin.ipEp);
            }
        }
        /// <summary>
        /// Gets the jump code from the admin
        /// </summary>
        /// <param name="contestPacket"></param>
        public void StartContestGetJumpCode(ref ContestPacket contestPacket)
        {
            contestPacket.contestMessages.StartListen();
            //Get the jumpCode
            while (true)
            {
                //Sending contestant id + nationality + jumpNr
                SendMessage("RequestJumpCodeFor@@" + contestPacket.contest.contestantList[contestPacket.contest.activeDiver].id + '&'
                    + contestPacket.contest.contestantList[contestPacket.contest.activeDiver].name + '&'
                    + contestPacket.contest.contestantList[contestPacket.contest.activeDiver].nationality + '&'
                    + (contestPacket.contest.activeJump+1).ToString() + '&', contestPacket.Admin.ipEp);

                Thread.Sleep(2000);
                if (contestPacket.contestMessages.Count > 0)
                {
                    Message message = contestPacket.contestMessages.Dequeue();
                    if (message.judge.username == contestPacket.Admin.username)
                    {
                        if (contestPacket.contest.rules.DegreeOfDifficulty(message.message) > 0)
                        {
                            contestPacket.contest.activeJumpCode = message.message;
                            break;
                        }
                    }
                }
                //Check if judges is online
                StartContestCheckJudgesOnline(ref contestPacket);
            }

            contestPacket.contestMessages.StopListen();
        }
        public void StartContestGetScoring(ref ContestPacket contestPacket)
        {
            //Broadcast diver and jumpcode and get replys.
            List<Judge> replys = new List<Judge>(contestPacket.judges);
            List<Score> scoreList = new List<Score>();
            contestPacket.contestMessages.StartListen();
            while (replys.Count > 0)
            {
                //Broadcast diver information and jumpcode to judges
                Broadcast("NewJumpInfo@@" + contestPacket.contest.contestantList[contestPacket.contest.activeDiver].id.ToString() + '&' + contestPacket.contest.activeJumpCode + '&'
                      + (contestPacket.contest.activeJump + 1), contestPacket.judges);

                while (contestPacket.contestMessages.Count > 0)
                {
                    Message message = contestPacket.contestMessages.Dequeue();
                    if (message.judge.username != contestPacket.Admin.username)
                    {
                        //If judge have not scored the jump yet, add jump
                        try
                        {
                            replys.Remove(replys.Find(x => x.username == message.judge.username));

                            try
                            {
                                Double dScore;
                                Double.TryParse(message.message, out dScore);
                                //Add score to contestantlist
                                Score tempScore = new Score(dScore, message.judge.username);
                                scoreList.Add(tempScore);

                                //Let admin know scored was recieved
                                SendMessage("SCORE@" + message.judge.username + "@" + dScore.ToString(), contestPacket.Admin.ipEp);
                                //Ack to sending judge.
                                SendMessage("ACK@@" + dScore.ToString(), message.judge.ipEp);
                                //remove judge from list to avoid scoring duplication
                                replys.Remove(replys.Find(x => x.username == message.judge.username));
                            }
                            catch (Exception e)
                            {
                                Console.WriteLine(e.Message);
                            }
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e.Message);
                        }

                    }
                }
                //Check if judges is online
                StartContestCheckJudgesOnline(ref contestPacket);
                //Check how many judges have answered, if all have, break.
                if (replys.Count == 0)
                {
                    break;
                }

            }
            contestPacket.contestMessages.StopListen();
            //Add jump
            contestPacket.contest.contestantList[contestPacket.contest.activeDiver].AddJump(contestPacket.contest.activeJumpCode, scoreList, contestPacket.contest.rules.height,
                    contestPacket.contest.rules.DegreeOfDifficulty(contestPacket.contest.activeJumpCode));
        }
        public void StartContest(ref ContestPacket contestPacket)
        {
            Console.WriteLine("Contest started.");
            Broadcast("CompetitionStarted@@", contestPacket.judges);
            SendMessage("CompetitionStarted@@", contestPacket.Admin.ipEp);

            contestPacket.started = true;
            bool runContest = true;
            //try
            //{
                while (runContest)
                {
                    //Get jumpCode for active jump
                    StartContestGetJumpCode(ref contestPacket);

                    StartContestGetScoring(ref contestPacket);

                    //Increment active diver and/or jump
                    if (contestPacket.contest.incrementActiveDiver() == 1)
                        runContest = false;
                }
            //}
            //catch (Exception e)
            //{
            //    Console.WriteLine(e.Message);
            //}

            Console.WriteLine("Competition " + contestPacket.id + " ended.");
            //Broadcast competition ended to connected clients

            Broadcast("CompetitionEnded@SERVER@", contestPacket.judges);
            SendMessage("CompetitionEnded@SERVER@", contestPacket.Admin.ipEp);

            //Move judges
            MoveFromActiveToAvailable(contestPacket);

            contestPacket.contest.contestantList.SortList();
            SaveContest(contestPacket);

            ContestPacket temp = contestPacket;

            activeContestPackets.Remove(activeContestPackets.Find(x => x.GUID == temp.GUID));

        }
        public void SaveContest(ContestPacket contestPacket)
        {
            resultsList.Add(contestPacket);

            SaveResultsXMLFile(filePathXMLResults);
        }
        public void SaveResultsXMLFile(string filePath)
        { 
                System.Xml.Serialization.XmlSerializer writer = 
                    new System.Xml.Serialization.XmlSerializer(typeof(List<ContestPacket>));

                System.IO.StreamWriter file = new System.IO.StreamWriter(
                    filePath);
                writer.Serialize(file, resultsList);
                file.Close();
        }
        public void LoadResultsXMLFile(string filePath)
        {
            try
            {
                System.Xml.Serialization.XmlSerializer reader =
                    new System.Xml.Serialization.XmlSerializer(typeof(List<ContestPacket>));
                System.IO.StreamReader file = new System.IO.StreamReader(
                   filePath);
                resultsList = (List<ContestPacket>)reader.Deserialize(file);
                file.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Data);
            }   
            
        }
        public void Broadcast(String message, List<Judge> clients, int wait = 2000)
        {
            foreach (Judge judge in clients)
            {
                SendMessage(message, judge.ipEp);
            }

            Thread.Sleep(3000);
        }
        /// <summary>
        /// Checks if the judges in the list are available. Those who are gets returned.
        /// </summary>
        /// <param name="usernames"></param>
        /// <returns></returns>
        public List<String> JudgesAvailable(List<String> usernames)
        {
            List<String> available = new List<String>();
            foreach (string username in usernames)
            {
                foreach (Judge judge in availableJudgePool)
                {
                    if (judge.username == username)
                    {
                        available.Add(judge.username);
                    }
                }
            }
            return available;
        }
        public List<Judge> MoveFromAvailableToActive(List<String> usernames)
        {
            List<Judge> toReturn = new List<Judge>();

            foreach (String username in usernames)
            {
                foreach (Judge judge in availableJudgePool)
                {
                    if (judge.username == username)
                    {
                        activeJudgePool.Add(judge);
                        toReturn.Add(judge);
                        availableJudgePool.Remove(judge);
                        break;
                    }
                }

            }
            return toReturn;
        }
        public void SortContestMessages(Message message)
        {
            try
            {
                activeContestPackets.Find(x => x.id == message.judge.username).contestMessages.Enqueue(message);
            }

            catch
            {
                for (int i = 0; i < activeContestPackets.Count; i++)
                {
                    for (int j = 0; j < activeContestPackets[i].judges.Count; j++)
                    {
                        if (message.judge.username == activeContestPackets[i].judges[j].username)
                            activeContestPackets[i].contestMessages.Enqueue(message);
                    }
                }
            }
        }
        public void LoadClientLogin(string filepath)
        {
            String line = "";
            try
            {
                System.IO.StreamReader file = new System.IO.StreamReader(filepath);

                while ((line = file.ReadLine()) != null)
                {
                    String[] split = line.Split('@');
                    logins.Add(split[0], new PassAndAccess(split[1], int.Parse(split[2])));
                }
                file.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }


        }
        public void ConsoleInput()
        {
            Console.WriteLine("Please press enter.");
            while (true)
            {
                try
                {
                    String command = Console.ReadLine();
                    String[] split = command.Split(' ');


                    if (split[0] == "-add")
                    {
                        if (split[1] == "j")
                        {
                            AppendStringToTxt(split[2] + '@' + split[3] + '@' + "1", filepathLogin);
                            logins.Add(split[2], new PassAndAccess(split[3], 1));
                        }
                        else if(split[1] == "a")
                        {
                            AppendStringToTxt(split[2] + '@' + split[3] + '@' + "2", filepathLogin);
                            logins.Add(split[2], new PassAndAccess(split[3], 2));
                        }
                    }
                    else if (split[0] == "-reset")
                    {
                        //Reset server
                    }
                    else if (split[0] == "-clients")
                    {
                        Console.WriteLine("Connected clients");
                        foreach (var client in activeJudgePool)
                        {
                            Console.WriteLine("Judge: Username: " + client.username + " IP:" + client.ipEp.Address);
                        }
                        foreach (var client in availableJudgePool)
                        {
                            Console.WriteLine("Judge: Username: " + client.username + " IP:" + client.ipEp.Address);
                        }
                        foreach (var client in adminPool)
                        {
                            Console.WriteLine("Admin: Username: " + client.username + " IP:" + client.ipEp.Address);
                        }
                    }
                    else if (split[0] == "-help")
                    {

                        Console.WriteLine("\nCommands");
                        Console.WriteLine("-reset : Resets the server.");
                        Console.WriteLine("-add : -add c username password, c can be j (judge) or a (admin).");
                        Console.WriteLine("-clients : Lists the current connected clients.");
                        Console.WriteLine("-exit : Shut the server down.");

                    }
                    else if (split[0] == "-exit")
                    {
                        //Exit server
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine("Invalid command.");
                    Console.WriteLine(e.Data);
                }
            }
        }
        public void AppendStringToTxt(string toAdd, string filePath)
        {
            if (!File.Exists(filePath))
            {
                StreamWriter sw = File.CreateText(filePath);
            }
            using (System.IO.StreamWriter file = File.AppendText(filePath))
            {
                file.WriteLine(toAdd);
                file.Close();
            }
        }
        public void MoveFromActiveToAvailable(ContestPacket contestPacket)
        {
            foreach (Judge j in contestPacket.judges)
            {
                try
                {
                    foreach(Judge j2 in activeJudgePool)
                    {
                        lock (activeJudgePool)
                        {
                            if (j.username == j2.username)
                            {
                                activeJudgePool.Remove(j2);
                                availableJudgePool.Add(j2);
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message + " |||MoveFromActiveToAvailable");
                }
            }
        }
        public class PassAndAccess
        {
            public String password;
            public int access;
            public PassAndAccess(String password, int access)
            {
                this.password = password;
                this.access = access;
            }
        }
    }
}

